package com.lollipop.juc;
/***
 *      ┌─┐       ┌─┐ + +
 *   ┌──┘ ┴───────┘ ┴──┐++
 *   │                 │
 *   │       ───       │++ + + +
 *   ███████───███████ │+
 *   │                 │+
 *   │       ─┴─       │
 *   │                 │
 *   └───┐         ┌───┘
 *       │         │
 *       │         │   + +
 *       │         │
 *       │         └──────────────┐
 *       │                        │
 *       │                        ├─┐
 *       │                        ┌─┘
 *       │                        │
 *       └─┐  ┐  ┌───────┬──┐  ┌──┘  + + + +
 *         │ ─┤ ─┤       │ ─┤ ─┤
 *         └──┴──┘       └──┴──┘  + + + +
 *                神兽保佑
 *               代码无BUG!
 */


import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @Author: J.K
 * @Date: 2023-01-12 14:18
 * @Description: 手写线程池
 */
public class MyThreadPoolExecutor implements Executor {
    /**
     * 记录线程池中线程数量
     */
    private final AtomicInteger ctl = new AtomicInteger(8);
    /**
     * 阻塞队列
     */
    private final BlockingQueue<Runnable> workQueue;
    /**
     * 核心线程数
     */
    private volatile int corePoolSize;
    /**
     * 最大线程数
     */
    private volatile int maxiumPoolSize;

    public MyThreadPoolExecutor(int corePoolSize, int maxiumPoolSize, BlockingQueue<Runnable> workQueue) {
        this.corePoolSize = corePoolSize;
        this.maxiumPoolSize = maxiumPoolSize;
        this.workQueue = workQueue;
    }

    public static void main(String[] args) {
        MyThreadPoolExecutor myThreadPoolExecutor = new MyThreadPoolExecutor(2, 2, new ArrayBlockingQueue<>(10));
        for (int i = 0; i < 10; i++) {
            final int taskNum = i;
            myThreadPoolExecutor.execute(() -> {
                try {
                    Thread.sleep(1500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("任务编号： " + taskNum);
            });
        }
    }


    @Override
    public void execute(Runnable command) {
        // 工作线程数
        int threadCount = ctl.get();

        // 小于核心线程数
        if (threadCount < corePoolSize) {
            // 添加任务失败
            if (!addWorker(command)) {
                // 执行拒绝策略
                reject();
            }
            return;
        }
        // 任务队列添加任务
        if (!workQueue.offer(command)) {
            // 任务队列满，尝试启动线程添加任务
            if (!addWorker(command)) {
                // 执行拒绝策略
                reject();
            }
        }
    }

    /**
     * 拒绝策略
     */
    private void reject() {
        throw new RuntimeException("can not execute! ctl.count: " + ctl.get() + "workQueue size: " + workQueue.size());
    }

    /**
     * 添加线程
     *
     * @param task
     * @return
     */
    private boolean addWorker(Runnable task) {
        // 若超过最大线程池数量直接返回false
        if (ctl.get() >= maxiumPoolSize) {
            return false;
        }

        Worker worker = new Worker(task);
        worker.thread.start();
        ctl.incrementAndGet();
        return true;
    }

    public class Worker implements Runnable {

        final Thread thread;
        Runnable task;

        public Worker(Runnable task) {
            this.thread = new Thread(this);
            this.task = task;
        }

        @Override
        public void run() {
            Runnable task = this.task;
            try {
                while (task != null || (task = getTask()) != null) {
                    task.run();
                    if (ctl.get() > maxiumPoolSize) {
                        break;
                    }
                    task = null;
                }
            } finally {
                ctl.decrementAndGet();
            }
        }

        /**
         * 从队列中获取数据
         *
         * @return
         */
        private Runnable getTask() {
            for (; ; ) {
                try {
                    System.out.println("workQueue size: " + workQueue.size());
                    return workQueue.take();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
